package org.walkerljl.boss.support.mvc.controller;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.support.RequestContextUtils;
import org.walkerljl.boss.support.common.log.SysLog;
import org.walkerljl.boss.support.domain.BaseDomain;
import org.walkerljl.boss.support.domain.ViewResult;
import org.walkerljl.boss.support.enums.Status;
import org.walkerljl.boss.support.enums.SysLogType;
import org.walkerljl.boss.support.mvc.MvcSupporter;
import org.walkerljl.boss.support.mvc.ServletContext;
import org.walkerljl.boss.support.mvc.entity.ObjectIdentifier;
import org.walkerljl.boss.support.mvc.i18n.I18N;
import org.walkerljl.boss.support.mvc.i18n.MessageResourceService;
import org.walkerljl.boss.support.mvc.util.MvcUtils;
import org.walkerljl.boss.support.mvc.velocity.VelocitySupport;
import org.walkerljl.boss.support.mvc.velocity.VelocityUtils;
import org.walkerljl.boss.support.sdk.sso.entity.User;
import org.walkerljl.commons.Constants;
import org.walkerljl.commons.config.ConfiguratorFactory;
import org.walkerljl.commons.datetime.DateUtils;
import org.walkerljl.commons.exception.AppException;
import org.walkerljl.commons.util.JSONUtils;
import org.walkerljl.commons.util.MapUtils;
import org.walkerljl.commons.util.StringPool;
import org.walkerljl.commons.util.StringUtils;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Date;
import java.util.Locale;
import java.util.Map;

/**
 * BaseController
 *
 * @author lijunlin
 */
public abstract class BaseController extends VelocitySupport {

    /**
     * 菜单条Key
     */
    private static final String APP_MENUBAR_KEY = "appMenu";

    /**
     * 应用名称
     */
    @Value(value = "${app.code}")
    private String appCode;
    /**
     * 应用名称
     */
    @Value(value = "${app.name}")
    private String appName;
    @Value(value = "${static.resource.address}")
    private String staticResourceAddress;

    /**
     * 对象标识符
     */
    private ObjectIdentifier objectIdentifier;
    /**
     * 　默认布局模板
     */
    private String layout = "layout/default";

    @Resource
    private MvcSupporter mvcSupporter;
    @Resource
    private MessageResourceService messageResourceService;

    public BaseController() {

    }

    protected Locale getLocale() {
        HttpServletRequest request = getRequest();
        return RequestContextUtils.getLocale(request);
    }

    /**
     * Get resource message
     *
     * @param code
     * @param args
     * @return
     */
    protected String getMessage(String code, Object... args) {
        return messageResourceService.get(code, args);
    }

    @Override
    public Map<String, Object> getDefaultContext() {
        Map<String, Object> context = MapUtils.newHashMap();
        String contextPath = ServletContext.getContextPath();
        context.put("contextPath", contextPath);
        if (StringUtils.isNotBlank(staticResourceAddress)) {
            context.put("staticRes", staticResourceAddress);
            context.put("localStaticRes", contextPath + "/resource");
        } else {
            context.put("staticRes", contextPath + "/resource");
        }
        context.put("v", Constants.V);
        context.put("utils", VelocityUtils.class);
        context.put("dateUtils", DateUtils.class);
        User currentUser = getCurrentUser();
        context.put("currentUser", currentUser);
        context.put("objectIdentifier", objectIdentifier);
        context.put("js_ns", ConfiguratorFactory.getIns().getStdConfigurator().getAsString("js.ns", "JARVIS"));
        context.put("ssoLoginAddress", mvcSupporter.getSsoLoginAddress());
        context.put("appName", getMessage("app.name"));
        context.put("appCode", getAppCode());
        context.put("currentYear", DateUtils.dateFormat(new Date(), "yyyy"));
        context.put("isJarvisJsDebugEnabled", ConfiguratorFactory.getIns().getStdConfigurator().getAsBoolean("isJarvisJsDebugEnabled", false));
        if (objectIdentifier != null && objectIdentifier.isLoadMenuBar() && currentUser != null) {//是否加载菜单条
            String uri = getRequest().getRequestURI().replaceAll(contextPath, "");
            String menuBarString = mvcSupporter.getMenuBar(currentUser.getUserId(), contextPath, uri);
            if (StringUtils.isNotBlank(menuBarString)) {
                context.put(APP_MENUBAR_KEY, menuBarString);
            }
        }
        context.put("isLogin", mvcSupporter.isLogin());
        Map<String, Object> bussinessContext = mvcSupporter.getBussinessContext();
        if (bussinessContext != null) {
            context.putAll(bussinessContext);
        }
        //i18n
        context.put("i18n", I18N.class);
        return context;
    }

    /**
     * 获取系统编码
     *
     * @return
     */
    private String getAppCode() {
        return ConfiguratorFactory.getIns().getStdConfigurator().getAsString("app.code");
    }

    /**
     * 获取Servlet Request对象
     *
     * @return
     */
    public HttpServletRequest getRequest() {
        return ServletContext.getRequest();
    }

    /**
     * 获取Servlet Response对象
     *
     * @return
     */
    public HttpServletResponse getResponse() {
        return ServletContext.getResponse();
    }

    /**
     * 重定向
     *
     * @param url
     * @param message
     */
    public void sendRedirect(String url, String message) {
        MvcUtils.sendRedirect(url, message);
    }

    /**
     * 重定向
     *
     * @param url
     */
    public void sendRedirect(String url) {
        MvcUtils.sendRedirect(url, null);
    }

    /**
     * 输出数据到模板,可指定默认布局
     *
     * @param layout 默认布局
     * @param view   模块
     * @param model  数据模型
     * @return
     */
    protected ModelAndView toViewResult(String layout, String view, Object model) {
        return toVM(layout, view, model);
    }

    /**
     * 输出数据到模板
     *
     * @param view  模板
     * @param model 数据模型
     * @return
     */
    protected ModelAndView toViewResult(String view, Object model) {
        return toVM(layout, view, model);
    }

    /**
     * 输出数据到模板
     *
     * @param view 模板
     * @return
     */
    protected ModelAndView toViewResult(String view) {
        return toVM(layout, view, null);
    }

    /**
     * 输出数据到模板,跳过默认布局
     *
     * @param view 模板
     * @return
     */
    protected ModelAndView toViewResultSkipLayout(String view) {
        return toViewResultSkipLayout(view, null);
    }

    /**
     * 输出数据到模板,跳过默认布局
     *
     * @param view  模板
     * @param model 数据模型
     * @return
     */
    protected ModelAndView toViewResultSkipLayout(String view, Object model) {
        return toVM(null, view, model);
    }

    /**
     * 数据JSON格式数据
     *
     * @param data   数据模型
     * @param format key:bean自身属性,value:转换后的属性 orgID -> id, 不需要转换就传递null
     * @return
     */
    protected final ModelAndView toSimpleJSON(Object data, Map<String, String> format) {
        PrintWriter responseWriter = null;
        try {
            responseWriter = getResponse().getWriter();
        } catch (IOException e) {
            throw new AppException(e);
        }
        if (data != null && data instanceof ViewResult) {
            JSONUtils.write(responseWriter, ((ViewResult) data).getMap(), format);
        } else {
            JSONUtils.write(responseWriter, data, format);
        }
        return null;
    }

    /**
     * 输出JSON格式数据
     *
     * @param data 数据模型
     * @return
     */
    protected final ModelAndView toSimpleJSON(Object data) {
        return toSimpleJSON(data, null);
    }

    /**
     * 输出JSON格式数据,JSON格式经过包装
     *
     * @param data   数据模型
     * @param flag   页面提示标志,true:操作成功,false:操作失败
     * @param msg    页面提示信息
     * @param format JSON格式化
     * @return
     */
    protected final ModelAndView toJSON(Object data, boolean flag, String msg, Map<String, String> format) {
        if (null == data) {
            data = MapUtils.newHashMap();
        }
        Map<String, Object> context = MapUtils.newHashMap();
        context.put(StringPool.RESPONSE_STATUS_KEY, flag);
        context.put(StringPool.RESPONSE_MESSAGE_KEY, msg);
        if (data != null && data instanceof ViewResult) {
            context.put(StringPool.RESPONSE_DATA_KEY, ((ViewResult) data).getMap());
        } else {
            context.put(StringPool.RESPONSE_DATA_KEY, data);
        }
        JSONUtils.write(getResponseWriter(), context, format);
        return null;
    }

    /**
     * 输出JSON格式数据,JSON格式经过包装
     *
     * @param data 数据模型
     * @param flag 页面提示标志,true:操作成功,false:操作失败
     * @param msg  页面提示信息
     * @return
     */
    protected final ModelAndView toJSON(Object data, boolean flag, String msg) {
        return toJSON(data, flag, msg, null);
    }

    /**
     * 输出JSON格式数据,JSON格式经过包装
     *
     * @param data 数据模型
     * @param flag 页面提示标志,true:操作成功,false:操作失败
     * @return
     */
    protected final ModelAndView toJSON(Object data, boolean flag) {
        return toJSON(data, flag, flag ? "操作成功" : "操作失败", null);
    }

    /**
     * 输出JSON格式数据,JSON格式经过包装
     *
     * @param data   数据模型
     * @param format JSON格式化
     * @return
     */
    protected final ModelAndView toJSON(Object data, Map<String, String> format) {
        return toJSON(data, true, StringPool.EMPTY, format);
    }

    /**
     * 输出JSON格式数据,JSON格式经过包装
     *
     * @param data 数据模型
     * @return
     */
    protected final ModelAndView toJSON(Object data) {
        return toJSON(data, true, StringPool.EMPTY, null);
    }

    protected SysLog buildSysLog(String request, SysLogType type) {
        return buildSysLog(request, null, type);
    }

    protected SysLog buildSysLog(String request, String response, SysLogType type) {
        SysLog sysLog = new SysLog();
        sysLog.setKeyword(objectIdentifier.getName() + "_" + type.getValue());
        sysLog.setRequest(request);
        sysLog.setResponse(response);
        sysLog.setOperator(getCurrentUserId());
        sysLog.setOperated(new Date());
        return sysLog;
    }

    /**
     * 获取模板
     *
     * @param templateName
     * @return
     */
    protected String getTemplate(String templateName) {
        return getTemplate(objectIdentifier.getTemplatePath(), templateName);
    }

    /**
     * 获取模板
     *
     * @param templateBasePath
     * @param templateName
     * @return
     */
    protected String getTemplate(String templateBasePath, String templateName) {
        StringBuilder sb = new StringBuilder();
        sb.append(templateBasePath).append(StringPool.SLASH).append(templateName);
        return sb.toString();
    }

    /**
     * 初始化对象
     *
     * @param baseDomain
     */
    protected void initBaseDomain(BaseDomain baseDomain) {
        if (baseDomain == null) {
            return;
        }
        User user = getCurrentUser();
        if (user != null) {
            baseDomain.setCreator(user.getUserId());
        }
    }

    /**
     * 创建对象时初始化基础模型
     *
     * @param baseDomain
     */
    protected void initBaseDomainWhenCreate(BaseDomain baseDomain) {
        if (baseDomain == null) {
            return;
        }
        User user = getCurrentUser();
        if (user != null) {
            baseDomain.setCreator(user.getUserId());
            baseDomain.setModifier(baseDomain.getCreator());
        }
        baseDomain.setCreated(new Date());
        baseDomain.setModified(baseDomain.getCreated());
        baseDomain.setStatusType(Status.ENABLED);
    }

    /**
     * 获取当前登录用户
     *
     * @return
     */
    protected User getCurrentUser() {
        return mvcSupporter.getCurrentUser();
    }

    /**
     * 获取当前登录用户Id
     *
     * @return
     */
    protected String getCurrentUserId() {
        User currentUser = getCurrentUser();
        return currentUser == null ? null : currentUser.getUserId();
    }

    /**
     * 获取对象标识符
     *
     * @return
     */
    public ObjectIdentifier getObjectIdentifier() {
        return objectIdentifier;
    }

    /**
     * 设置对象标识符
     *
     * @param objectIdentifier
     */
    public void setObjectIdentifier(ObjectIdentifier objectIdentifier) {
        this.objectIdentifier = objectIdentifier;
        if (StringUtils.isNotBlank(objectIdentifier.getLayout())) {
            layout = objectIdentifier.getLayout();
        }
    }

    /**
     * getResponseWriter
     *
     * @return
     */
    protected PrintWriter getResponseWriter() {
        HttpServletResponse response = getResponse();
        try {
            return response == null ? null : response.getWriter();
        } catch (IOException e) {
            throw new AppException(e);
        }
    }
}